<?php

namespace Xin\ThinkPHP\Model;

use think\db\exception\DataNotFoundException;
use think\db\exception\ModelNotFoundException;
use think\exception\ValidateException;
use think\Validate;

trait HasSetValue
{

    /**
     * 设置当前实例字段值
     * @param string $field
     * @param mixed $value
     * @return bool
     */
    public function setValue($field, $value)
    {
        if (!static::isAllowSetField($field)) {
            throw new ValidateException("{$field} not in allow field list.");
        }

        // 验证规则
        $allowSetFields = static::getAllowSetFields();
        if (isset($allowSetFields[$field]) && ($validateRule = $allowSetFields[$field])) {
            (new Validate)->failException(true)->check([
                $field => $value,
            ], [
                $field => $validateRule,
            ]);
        }

        return $this->save([$field => $value]);
    }

    /**
     * 设置多条记录字段值
     * @param array|callable $data
     * @param string $field
     * @param mixed $value
     * @return \think\Collection
     * @throws DataNotFoundException
     * @throws ModelNotFoundException
     * @throws \think\db\exception\DbException
     */
    public static function setManyValue($data, $field, $value, $itemCallback = null)
    {
        return (new static)->where(is_array($data) ? [
            ['id', 'IN', $data],
        ] : $data)->select()->each(function (self $item) use ($field, $value, $itemCallback) {
            $flag = $item->setValue($field, $value);
            if ($itemCallback) {
                $itemCallback($flag);
            }
        });
    }

    /**
     * 是否允许设置字段
     *
     * @param string $field
     * @return bool
     */
    public static function isAllowSetField($field)
    {
        $allowSetFields = static::getAllowSetFields();

        return in_array($field, array_map('strval', array_keys($allowSetFields)), true);
    }

    /**
     * 获取允许修改字段
     *
     * @return array
     */
    public static function getAllowSetFields()
    {
        return [
            'status' => 'in:1,2',
        ];
    }
}